﻿using System;
using System.Threading;

namespace Neovolve.Extensibility.VisualStudio.ReflectorLink
{
    internal class DelegateInvoker
    {
        public delegate void InvokeVoidDelegate();

        public delegate void InvokeVoidParameterDelegate(params Object[] args);

        private Object[] _args;

        private Exception _exception;

        private InvokeVoidDelegate _voidMethod;

        private InvokeVoidParameterDelegate _voidParameterMethod;

        public void RunDelegate(InvokeVoidParameterDelegate method, params Object[] args)
        {
            RunDelegate(method, args, Timeout.Infinite);
        }

        public void RunDelegate(InvokeVoidParameterDelegate method, Int32 timeout, params Object[] args)
        {
            _voidParameterMethod = method;
            _args = args;

            InvokeDelegate(timeout);
        }

        public void RunDelegate(InvokeVoidDelegate method)
        {
            RunDelegate(method, Timeout.Infinite);
        }

        public void RunDelegate(InvokeVoidDelegate method, Int32 timeout)
        {
            _voidMethod = method;

            InvokeDelegate(timeout);
        }

        private void InvokeDelegate(Int32 timeout)
        {
            Thread newThread = new Thread(RunInternal);

            newThread.IsBackground = true;
            newThread.Start();

            Boolean methodCompleted = newThread.Join(timeout);

            if (methodCompleted == false)
            {
                newThread.Abort();

                throw new TimeoutException();
            }

            if (_exception != null)
            {
                throw _exception;
            }
        }

        private void RunInternal()
        {
            try
            {
                if (_voidMethod != null)
                {
                    _voidMethod();
                }

                if (_voidParameterMethod != null)
                {
                    _voidParameterMethod(_args);
                }
            }
            catch (Exception ex)
            {
                _exception = ex;
            }
        }
    }

    internal class DelegateInvoker<T>
    {
        public delegate T InvokeReturnDelegate();

        public delegate T InvokeReturnParameterDelegate(params Object[] args);

        private Object[] _args;

        private Exception _exception;

        private T _returnValue;

        private InvokeReturnDelegate _voidMethod;

        private InvokeReturnParameterDelegate _voidParameterMethod;

        public T RunDelegate(InvokeReturnParameterDelegate method, params Object[] args)
        {
            return RunDelegate(method, args, Timeout.Infinite);
        }

        public T RunDelegate(InvokeReturnParameterDelegate method, Int32 timeout, params Object[] args)
        {
            _voidParameterMethod = method;
            _args = args;

            return InvokeDelegate(timeout);
        }

        public T RunDelegate(InvokeReturnDelegate method)
        {
            return RunDelegate(method, Timeout.Infinite);
        }

        public T RunDelegate(InvokeReturnDelegate method, Int32 timeout)
        {
            _voidMethod = method;

            return InvokeDelegate(timeout);
        }

        private T InvokeDelegate(Int32 timeout)
        {
            Thread newThread = new Thread(RunInternal);

            newThread.IsBackground = true;
            newThread.Start();

            Boolean methodCompleted = newThread.Join(timeout);

            if (methodCompleted == false)
            {
                newThread.Abort();

                throw new TimeoutException();
            }

            if (_exception != null)
            {
                throw _exception;
            }

            return _returnValue;
        }

        private void RunInternal()
        {
            try
            {
                if (_voidMethod != null)
                {
                    _returnValue = _voidMethod();
                }

                if (_voidParameterMethod != null)
                {
                    _returnValue = _voidParameterMethod(_args);
                }
            }
            catch (Exception ex)
            {
                _exception = ex;
            }
        }
    }
}